package com.authority.service.impl;

import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.Resource;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.springframework.stereotype.Service;

import com.authority.common.AdminConstants;
import com.authority.common.AuthConstants;
import com.authority.dao.AuthActionDAO;
import com.authority.dao.AuthMenuActionDAO;
import com.authority.dao.AuthMenuDAO;
import com.authority.dao.AuthRoleDAO;
import com.authority.dao.AuthRoleMenuDAO;
import com.authority.dao.AuthSysDAO;
import com.authority.dao.AuthUserRoleDAO;
import com.authority.entry.AuthActionDO;
import com.authority.entry.AuthMenuActionDO;
import com.authority.entry.AuthMenuDO;
import com.authority.entry.AuthRoleMenuDO;
import com.authority.entry.AuthUserRoleDO;
import com.authority.result.UserPrivilegeDTO;
import com.authority.service.UserPrivilegeService;
import com.authority.utils.CommonUtils;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;

/**
 * @author limeng.g
 */
@Service("userPrivilegeService")
public class UserPrivilegeServiceImpl implements UserPrivilegeService {

    @Resource
    private AuthSysDAO authSysDAO;

    @Resource
    private AuthUserRoleDAO authUserRoleDAO;

    @Resource
    private AuthRoleDAO authRoleDAO;

    @Resource
    private AuthRoleMenuDAO authRoleMenuDAO;

    @Resource
    private AuthMenuDAO authMenuDAO;

    @Resource
    private AuthActionDAO authActionDAO;

    @Resource
    private AuthMenuActionDAO authMenuActionDAO;

    @Override
    public List<UserPrivilegeDTO> getUserPrivilegeList(Long userId, String sysCode) throws Exception {

        try {
            //step1 获取系统id
            Long sysId = authSysDAO.queryIdByCode(sysCode);

            //step2 获取用户的所有角色
            List<AuthUserRoleDO> urList = authUserRoleDAO.queryRoleByUserId(userId, sysId);

            //step3  获取角色具有的所有菜单和功能权限，相同的去重,并转换成UserPrivilegeDTO
            Map<Long, UserPrivilegeDTO> map = generateUserPrivilegeMap(urList, sysId);

            //step4 生成菜单树
            List<UserPrivilegeDTO> dtos = generateUserPrivilegeTree(map);

            //step5 对横向节点按顺序号进行排序
            sortChildren(dtos, new NumComparator());

            return dtos;
        } catch (Exception e) {
        	e.printStackTrace();
            throw new Exception("获取用户权限失败");
        }
    }

    /**
     * 获取用户权限的map集合
     *
     * @param urList 用户角色列表 
     * @param sysId  系统id
     * @return
     * @throws Exception
     */
    private Map<Long, UserPrivilegeDTO> generateUserPrivilegeMap(List<AuthUserRoleDO> urList, Long sysId) throws Exception {

        Map<Long, UserPrivilegeDTO> map = Maps.newHashMap();

        Multimap<Long, AuthRoleMenuDO>  rmMap = rmListToMap(authRoleMenuDAO.queryRoleMenuBySysId(sysId));
        Map<Long,AuthActionDO> aMap = aListToMap(authActionDAO.queryAuthActionBySysId(sysId));
        Map<Long,AuthMenuDO> mMap = mListToMap(authMenuDAO.queryAuthMenuBySysId(sysId));
        
        for (AuthUserRoleDO urdb : urList) {
            Collection<AuthRoleMenuDO> rmList = rmMap.get(urdb.getRoleId());

            if (rmList != null && rmList.size() > 0) {
                for (AuthRoleMenuDO rmdb : rmList) {
                	Long menuId = rmdb.getMenuId();
                    //不存在，直接添加，存在则需合并功能
                	UserPrivilegeDTO dto;
                    if (map.containsKey(menuId)) {
                    	dto = map.get(menuId);
                    } else {
                    	dto = CommonUtils.dbToVo(mMap.get(menuId), UserPrivilegeDTO.class);
                    }
                    actionsIdsToCodeSet(aMap,rmdb.getActionIds(), dto.getActionCodes());
                    map.put(menuId, dto);

                }
            }

        }

        return map;
    }

    /**
     * 把action id转换成action code并添加到set集合
     *
     * @param set
     * @return
     */
    private void actionsIdsToCodeSet( Map<Long,AuthActionDO> aMap,String actionIds, Set<String> set) {

        if (StringUtils.isNotEmpty(actionIds)) {
            for (String a : actionIds.split(",")) {
                set.add(aMap.get(NumberUtils.toLong(a)).getCode());
            }
        }
    }

    /**
     * 把map转换成tree
     *
     * @param map
     * @return
     */
    private List<UserPrivilegeDTO> generateUserPrivilegeTree(Map<Long, UserPrivilegeDTO> map) {

        List<UserPrivilegeDTO> dtos = Lists.newArrayList();
        for (Long key : map.keySet()) {
            UserPrivilegeDTO dto = map.get(key);
            if (AuthConstants.ROOT.equals(String.valueOf(dto.getParentId()))) {
                dtos.add(dto);
            } else {
            	if (map.get(dto.getParentId()) == null) {
            		continue;
            	}
                map.get(dto.getParentId()).addChildren(dto);
            }
        }
        return dtos;
    }

    /**
     * 对横向子节点进行排序
     *
     * @param children
     * @param c        比较器
     */
    private void sortChildren(List<UserPrivilegeDTO> children, Comparator<UserPrivilegeDTO> c) {

        Collections.sort(children, c);

        for (UserPrivilegeDTO dto : children) {
            if (dto.getChildren() != null && dto.getChildren().size() > 0) {
                Collections.sort(dto.getChildren(), c);
            }
        }
    }

    /**
     * 按顺序号进行排序
     */
    class NumComparator implements Comparator<UserPrivilegeDTO> {

        @Override
        public int compare(UserPrivilegeDTO o1, UserPrivilegeDTO o2) {
            return o1.getNum() - o2.getNum();
        }
    }

    /**
     * 获取用户权限URL
     */
    @Override
    public Set<String> getUserPrivilegeUrls(List<UserPrivilegeDTO> privileges)
            throws Exception {

        return addUrl(privileges, new HashSet<String>());
    }

    private Set<String> addUrl(List<UserPrivilegeDTO> list, Set<String> set) {

        if (list != null && list.size() > 0) {
            for (UserPrivilegeDTO dto : list) {
                String url = dto.getUrl();
                if (StringUtils.isNotEmpty(url)) {
                    set.add(url);
                }
                addUrl(dto.getChildren(), set);
            }
        }
        return set;
    }

    @Override
    public List<UserPrivilegeDTO> getAllUserPrivilegeList() throws Exception {

        //step1 获取系统id
        Long sysId = authSysDAO.queryIdByCode(AdminConstants.SYSTEM_CODE);

        //step2 获取系统所有菜单
        List<AuthMenuDO> mList = authMenuDAO.queryAuthMenuBySysId(sysId);

        //step3 添加功能，并生成UserPrivilegeDTO
        Map<Long, UserPrivilegeDTO> map = generateUserPrivilegeMap2(mList, sysId);

        //step4 生成菜单树
        List<UserPrivilegeDTO> dtos = generateUserPrivilegeTree(map);

        //step5 对横向节点按顺序号进行排序
        sortChildren(dtos, new NumComparator());

        return dtos;
    }

    /**
     * 获取用户权限的map集合（key为菜单id）
     * @param mList 菜单列表 
     * @param sysId 系统id
     * @return
     * @throws Exception
     */
    private Map<Long, UserPrivilegeDTO> generateUserPrivilegeMap2(List<AuthMenuDO> mList, Long sysId) throws Exception {
        Map<Long, UserPrivilegeDTO> map = Maps.newHashMap();

        Multimap<Long,AuthMenuActionDO> maMap  =  maListToMap(authMenuActionDAO.queryAuthMenuActionBySysId(sysId));
        Map<Long,AuthActionDO> aMap = aListToMap(authActionDAO.queryAuthActionBySysId(sysId));
        
        for (AuthMenuDO mdb : mList) {
            UserPrivilegeDTO dto = CommonUtils.dbToVo(mdb, UserPrivilegeDTO.class);

            Collection<AuthMenuActionDO> maList = maMap.get(mdb.getId());
            if (!CollectionUtils.isEmpty(maList)) {
                Set<String> actionCodes = Sets.newHashSet();
                for (AuthMenuActionDO madb : maList) {
                    actionCodes.add(aMap.get(madb.getActionId()).getCode());
                }
                dto.setActionCodes(actionCodes);
            }

            map.put(mdb.getId(), dto);
        }
        return map;
    }
    
    @Override
    public boolean checkActionPrivilege(Long userId, String sysCode, String pageCode, String actionCode) {

        Long sysId = authSysDAO.queryIdByCode(sysCode);
        Long menuId = authMenuDAO.queryAuthMenuByCodeAndSysId(pageCode, sysId);
        List<AuthUserRoleDO> urList = authUserRoleDAO.queryRoleByUserId(userId, sysId);
        
        Map<Long,AuthActionDO> aMap = aListToMap(authActionDAO.queryAuthActionBySysId(sysId));
        Multimap<Long, AuthRoleMenuDO>  rmMap = rmListToMap(authRoleMenuDAO.queryRoleMenuBySysId(sysId));

        for (AuthUserRoleDO urdb : urList) {
        	Collection<AuthRoleMenuDO> rmList = rmMap.get(urdb.getRoleId());
            Set<String> actionCodes = Sets.newHashSet();
            if (rmList != null && rmList.size() > 0) {
                for (AuthRoleMenuDO rmdb : rmList) {
                    if (rmdb.getMenuId().equals(menuId)) {
                        actionsIdsToCodeSet(aMap,rmdb.getActionIds(), actionCodes);
                    }
                }
            }

            if (actionCodes.contains(actionCode)) {
                return true;
            }
        }
        
        return false;
    }
    
    @Override
	public boolean checkPagePrivilege(Long userId, String sysCode, String pageCode) {
    	
    	Long sysId = authSysDAO.queryIdByCode(sysCode);
    	
    	Long menuId = authMenuDAO.queryAuthMenuByCodeAndSysId(pageCode, sysId);
    	if(menuId == null){
    		return false;
    	}
    	
        List<AuthUserRoleDO> urList = authUserRoleDAO.queryRoleByUserId(userId, sysId);
        Multimap<Long, AuthRoleMenuDO>  rmMap = rmListToMap(authRoleMenuDAO.queryRoleMenuBySysId(sysId));
        
        for (AuthUserRoleDO urdb : urList) {
        	Collection<AuthRoleMenuDO> rmList = rmMap.get(urdb.getRoleId());
            if (rmList != null && rmList.size() > 0) {
                for (AuthRoleMenuDO rmdb : rmList) {
                    if (rmdb.getMenuId().equals(menuId)) {
                    	return true;
                    }
                }
            }
        }
        
		return false;
	}
    
    private  Multimap<Long,AuthMenuActionDO> maListToMap(List<AuthMenuActionDO> sourceList){
    	
    	Multimap<Long, AuthMenuActionDO> map = HashMultimap.create();
    	for(AuthMenuActionDO madb : sourceList){
    		map.put(madb.getMenuId(),madb);
    	}
    	
    	return map;
    }
    
    private Multimap<Long, AuthRoleMenuDO> rmListToMap(List<AuthRoleMenuDO> sourceList){
    	
    	Multimap<Long, AuthRoleMenuDO> map = HashMultimap.create();
    	for(AuthRoleMenuDO rmdb : sourceList){
    		map.put(rmdb.getRoleId(),rmdb);
    	}
    	
    	return map;
    }
    
    private Map<Long,AuthActionDO> aListToMap(List<AuthActionDO> sourceList){
    	
    	Map<Long,AuthActionDO> map = Maps.newHashMap();
    	
		for(AuthActionDO adb : sourceList){
			map.put(adb.getId(),adb);
		}
    	
    	return map;
    }
    
    private Map<Long,AuthMenuDO> mListToMap(List<AuthMenuDO> sourceList){
    	
    	Map<Long,AuthMenuDO> map = Maps.newHashMap();
    	
		for(AuthMenuDO mdb : sourceList){
			map.put(mdb.getId(),mdb);
		}
    	
    	return map;
    }
}
